#include "stdafx.h"
#include "regkey.h"

namespace sysinfo { namespace windows {

RegKey::RegKey(HKEY key, REGSAM access_type/*=KEY_ALL_ACCESS*/)
	: key_(key)
	, access_type_(access_type)
	, error_(ERROR_SUCCESS)
{}

RegKey::~RegKey()
{
	::RegCloseKey(key_);
}

///////////////////////////////////////////////////////////////////////////////
bool RegKey::IsValid() const
{
	return key_ != NULL;
}

size_t RegKey::GetKeyCount() const
{
	DWORD count;
	error_ = ::RegQueryInfoKeyA(key_, NULL, NULL, NULL, &count, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
	if (ERROR_SUCCESS != error_)
		count = 0;
	return count;
}

size_t RegKey::GetValueCount() const
{
	DWORD count;
	error_ = ::RegQueryInfoKeyA(key_, NULL, NULL, NULL, NULL, NULL, NULL, &count, NULL, NULL, NULL, NULL);
	if (ERROR_SUCCESS != error_)
		count = 0;
	return count;
}

///////////////////////////////////////////////////////////////////////////////
RegKey RegKey::GetKey(const string& name) const
{	
	HKEY key = NULL;
	error_ = ::RegOpenKeyExA(key_, name.c_str(), NULL, access_type_, &key);
	return RegKey(key, access_type_);
}

string RegKey::GetValue(const string& name, DWORD& type) const
{
	string value;
	value.resize(MAX_PATH);
	DWORD size = value.size();
	error_ = ::RegQueryValueExA(key_, name.c_str(), NULL, &type, (LPBYTE)&value[0], &size);
	if (ERROR_SUCCESS != error_)
	{
		type =  REG_NONE;
		size = 0;
	}
    if (type == REG_SZ)
        --size;
	value.resize(size);
	return value;
}

bool RegKey::HasKey(const string& name) const
{
    GetKey(name);
    return error_ == ERROR_SUCCESS;
}

bool RegKey::HasValue(const string& name) const
{
    DWORD type;
    GetValue(name, type);
    return error_ == ERROR_SUCCESS;
}

///////////////////////////////////////////////////////////////////////////////
bool RegKey::EnumKey(int index, string& name, string& cls) const
{
	name.resize(MAX_PATH);
	cls.resize(MAX_PATH);
	DWORD name_size = name.size(), cls_size = cls.size();
	error_ = ::RegEnumKeyExA(key_, index, &name[0], &name_size, NULL, &cls[0], &cls_size, NULL);
	bool ok = (ERROR_SUCCESS == error_);
	if (!ok)
	{
		name_size = 0;
		cls_size = 0;
	}
	name.resize(name_size);
	cls.resize(cls_size);
	return ok;
}

bool RegKey::EnumValue(int index, string& name, DWORD& type, string& value) const
{
	name.resize(MAX_PATH);
	value.resize(MAX_PATH);
	DWORD name_size = name.size(), value_size = value.size();
	error_ = ::RegEnumValueA(key_, index, &name[0], &name_size, NULL, &type, (LPBYTE)&value[0], &value_size);
	bool ok = (ERROR_SUCCESS == error_);
	if (!ok)
	{
		name_size = 0;
		value_size = 0;
		type = REG_NONE;
	}
	name.resize(name_size);
	value.resize(value_size);
	return ok;
}

///////////////////////////////////////////////////////////////////////////////
RegKey RegKey::PutKey(const string& name, const string& cls/*=""*/) const
{
	HKEY key = NULL;
	error_ = ::RegCreateKeyExA(key_, name.c_str(), 0, const_cast<char*>(cls.c_str()), 0, access_type_, NULL, &key, NULL);
	return RegKey(key, access_type_);
}

bool RegKey::RemoveKey(const string& name) const
{
	RegKey sub = GetKey(name);
	if (sub.IsValid())
	{
		int index = 0;
		string name, cls;
		while (sub.EnumKey(index++, name, cls))
		{
			if (sub.RemoveKey(name))
				--index;
		}
	}
	error_ = ::RegDeleteKeyA(key_, name.c_str());
	return ERROR_SUCCESS == error_;
}

///////////////////////////////////////////////////////////////////////////////
bool RegKey::PutValue(const string& name, DWORD type, const string& value) const
{
	error_ = ::RegSetValueExA(key_, name.c_str(), NULL, type, (CONST BYTE*)&value[0], value.size());
	return ERROR_SUCCESS == error_;
}

bool RegKey::RemoveValue(const string& name) const
{
	error_ = ::RegDeleteValueA(key_, name.c_str());
	return ERROR_SUCCESS == error_;
}

RegKey& RegKey::operator=(const RegKey& rhs)
{
    HANDLE process = ::GetCurrentProcess();
    ::DuplicateHandle(process, rhs.key_, process, (HANDLE*)&key_, 
                      0, FALSE, DUPLICATE_SAME_ACCESS);
    ::CloseHandle(process);
    return *this;
}


}}